﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.Runtime.InteropServices;


namespace JFTest.Communacation
{
  
    public class HPDeviceCommunicator : IDisposable
    {
        private SerialPort _serialPort;
        private const byte Header = 0xAA;

        // 命令枚举
        public enum CMD_TYPE
        {
            CMD_POWEROFF = 0x01,
            CMD_SET_RESET,
            CMD_SET_ZERO,
            CMD_SET_UNIT,
            CMD_SET_PEAK_TRACK,
            CMD_GET_INFO,
            CMD_SET_AUTO_POWEROFF_TIME,
            CMD_SET_LIMIT_UP,
            CMD_SET_LIMIT_DOWN,
            CMD_SET_LIMIT_CMP,
            CMD_ENTER_REMOTE,
            CMD_EXIT_REMOTE,
        }

        // 模式枚举
        public enum MODE
        {
            MODE_TRACK = 0, // 显示average 跟踪模式
            MODE_PEAK,      // peak 峰值模式
        }

        // 单位枚举
        public enum UNIT
        {
            kgf_cm = 0,
            lbf_in,
            n_m,
            none
        }

        // 硬件信息结构体
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct hw_info_t
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
            public byte[] uid; // STC UID 7Byte

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
            public byte[] model; // HP-100 最大

            public UInt16 range; // 量程

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
            public byte[] ver; // 1.0.1 长度8字节
        }

        // 系统配置结构体
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct sys_cfg_t
        {
            public hw_info_t hw_info;
            public byte unit;      // 单位
            public byte mode;      // 模式
            public UInt16 sleep;   // 自动关机时间
            public UInt16 max_val;  // 最大值
            public UInt16 min_val;  // 最小值
            public UInt16 ave_val; // 比较值
            public UInt16 grv_val;
        }

        // 构造函数
        public HPDeviceCommunicator(string portName)
        {
            _serialPort = new SerialPort(portName)
            {
                BaudRate = 115200,
                Parity = Parity.None,
                DataBits = 8,
                StopBits = StopBits.One,
                Handshake = Handshake.None,
                ReadTimeout = 500,
                WriteTimeout = 500
            };
        }

        // 打开连接
        public void Open()
        {
            if (!_serialPort.IsOpen)
            {
                _serialPort.Open();
            }
        }

        // 关闭连接
        public void Close()
        {
            if (_serialPort.IsOpen)
            {
                _serialPort.Close();
            }
        }

        // 实现IDisposable接口
        public void Dispose()
        {
            Close();
            _serialPort.Dispose();
        }

        // 计算CRC16校验
        private ushort CalculateCRC16(byte[] data, int length)
        {
            ushort crc = 0xFFFF;

            for (int i = 0; i < length; i++)
            {
                crc ^= data[i];

                for (int j = 0; j < 8; j++)
                {
                    if ((crc & 0x0001) != 0)
                    {
                        crc = (ushort)((crc >> 1) ^ 0xA001);
                    }
                    else
                    {
                        crc >>= 1;
                    }
                }
            }

            return crc;
        }

        // 发送命令
        private void SendCommand(byte cmd, byte[] args = null)
        {
            if (!_serialPort.IsOpen)
            {
                throw new InvalidOperationException("串口未打开");
            }

            // 计算长度 (命令 + 参数)
            byte length = (byte)(1 + (args?.Length ?? 0));

            // 构建数据包
            byte[] packet = new byte[3 + (args?.Length ?? 0) + 2]; // 头+长度+命令+参数+CRC

            packet[0] = Header;  // 头
            packet[1] = length;  // 长度
            packet[2] = cmd;     // 命令

            // 添加参数
            if (args != null && args.Length > 0)
            {
                Array.Copy(args, 0, packet, 3, args.Length);
            }

            // 计算CRC (不包括头)
            ushort crc = CalculateCRC16(packet, packet.Length - 2);

            // 添加CRC (小端)
            packet[packet.Length - 2] = (byte)(crc & 0xFF);
            packet[packet.Length - 1] = (byte)((crc >> 8) & 0xFF);

            // 发送数据
            _serialPort.Write(packet, 0, packet.Length);
        }
        public void SendHexCmd(string cmd)
        {
            if (!_serialPort.IsOpen)
            {
            }
            cmd = "AA010672D0";//16进制字符串命令-》转成二进制字符串-》转为字节数组-》接收数据-》byte数组转16进制字符串
            //byte[] byteCmd = Encoding.UTF8.GetBytes(cmd);
            string binaryString = "1010101000000001000001100111001011010000"; // 将字符串数组合并成一个完整的二进制字符串。如果是整数数组，则需要先转换为字符串。例如：string.Join("", binaryArray.Select(b => b == 1 ? "1" : "0"))。

            byte[] byteCmd=BinaryStringToByteArray(binaryString);
            _serialPort.Write(byteCmd,0,byteCmd.Length);
        }

        public byte[] ReceiveHexData()
        {
            byte[] result = null;
            if (!_serialPort.IsOpen)
            {
            }

            int dataLen = _serialPort.BytesToRead;
            result = new byte[dataLen];
            _serialPort.Read(result, 0, dataLen);
            return result;
        }
        public static byte[] BinaryStringToByteArray(string binaryString)
        {
            // 计算字节数组的长度（每8个二进制位对应一个字节）
            int numOfBytes = binaryString.Length / 8;

            // 创建字节数组
            byte[] byteArray = new byte[numOfBytes];

            // 遍历二进制字符串的每8个字符，将其转换为一个字节并存储在字节数组中
            for (int i = 0; i < numOfBytes; i++)
            {
                // 从二进制字符串中提取8个字符作为一个字节的二进制表示
                string byteString = binaryString.Substring(i * 8, 8);

                // 将字节的二进制表示转换为一个字节，并存储在字节数组中
                byteArray[i] = Convert.ToByte(byteString, 2);
            }

            // 返回字节数组
            return byteArray;
        }

        // 接收数据
        private byte[] ReceiveData(int timeout = 10000)
        {
            if (!_serialPort.IsOpen)
            {
                throw new InvalidOperationException("串口未打开");
            }

            int oldTimeout = _serialPort.ReadTimeout;
            _serialPort.ReadTimeout = timeout;

            try
            {
                // 读取头
                byte header = (byte)_serialPort.ReadByte();
                if (header != Header)
                {
                    throw new InvalidOperationException("无效的响应头");
                }

                // 读取长度
                byte length = (byte)_serialPort.ReadByte();

                // 读取命令
                byte cmd = (byte)_serialPort.ReadByte();

                // 读取数据
                byte[] data = new byte[length + 2]; // 数据 + CRC
                int bytesRead = _serialPort.Read(data, 0, data.Length);

                if (bytesRead != data.Length)
                {
                    throw new InvalidOperationException("接收数据不完整");
                }

                // 验证CRC
                byte[] fullPacket = new byte[2 + length + 2]; // 头+长度+命令+数据+CRC
                fullPacket[0] = header;
                fullPacket[1] = length;
                fullPacket[2] = cmd;
                Array.Copy(data, 0, fullPacket, 3, data.Length);

                ushort receivedCrc = (ushort)(data[data.Length - 2] | (data[data.Length - 1] << 8));
                ushort calculatedCrc = CalculateCRC16(fullPacket, fullPacket.Length - 2);

                if (receivedCrc != calculatedCrc)
                {
                    throw new InvalidOperationException("CRC校验失败");
                }

                // 返回有效数据 (不包括CRC)
                byte[] result = new byte[length - 1 + data.Length - 2]; // 命令 + 数据
                result[0] = cmd;
                Array.Copy(data, 0, result, 1, data.Length - 2);

                return result;
            }
            finally
            {
                _serialPort.ReadTimeout = oldTimeout;
            }
        }

        // 反转UInt16字节序 (小端转大端)
        private UInt16 Uint16Reverse(UInt16 n)
        {
            byte[] b = new byte[2];
            b[0] = (byte)(n >> 8);
            b[1] = (byte)(n);
            return (UInt16)((b[1] << 8) | b[0]);
        }

        // 解析硬件信息
        public sys_cfg_t ParseSystemInfo(byte[] data)
        {
            IntPtr ptr = Marshal.AllocHGlobal(128); // 分配非托管内存空间
            try
            {
                Marshal.Copy(data, 0, ptr, Math.Min(data.Length, 128)); // 将数组数据拷贝到非托管内存指针
                sys_cfg_t sys_cfg = (sys_cfg_t)Marshal.PtrToStructure(ptr, typeof(sys_cfg_t));

                // 反转字节序
                sys_cfg.hw_info.range = Uint16Reverse(sys_cfg.hw_info.range);
                sys_cfg.max_val = Uint16Reverse(sys_cfg.max_val);
                sys_cfg.min_val = Uint16Reverse(sys_cfg.min_val);
                sys_cfg.ave_val = Uint16Reverse(sys_cfg.ave_val);
                sys_cfg.sleep = Uint16Reverse(sys_cfg.sleep);

                return sys_cfg;
            }
            finally
            {
                Marshal.FreeHGlobal(ptr); // 释放内存
            }
        }

        // 公共方法

        // 关机
        public void PowerOff()
        {
            SendCommand((byte)CMD_TYPE.CMD_POWEROFF);
        }

        // 复位
        public void Reset()
        {
            SendCommand((byte)CMD_TYPE.CMD_SET_RESET);
        }

        // 清零
        public void Zero()
        {
            SendCommand((byte)CMD_TYPE.CMD_SET_ZERO);
        }

        // 设置单位
        public void SetUnit(UNIT unit)
        {
            SendCommand((byte)CMD_TYPE.CMD_SET_UNIT, new byte[] { (byte)unit });
        }

        // 设置模式
        public void SetMode(MODE mode)
        {
            SendCommand((byte)CMD_TYPE.CMD_SET_PEAK_TRACK, new byte[] { (byte)mode });
        }

        // 获取设备信息
        public sys_cfg_t GetDeviceInfo()
        {
            SendCommand((byte)CMD_TYPE.CMD_GET_INFO);
            byte[] response = ReceiveData();
            return ParseSystemInfo(response);
        }

        // 设置自动关机时间
        public void SetAutoPowerOffTime(ushort minutes)
        {
            if (minutes > 120)
            {
                throw new ArgumentOutOfRangeException("关机时间必须在0-120分钟之间");
            }

            byte[] args = new byte[2];
            args[0] = (byte)(minutes >> 8);
            args[1] = (byte)(minutes & 0xFF);

            SendCommand((byte)CMD_TYPE.CMD_SET_AUTO_POWEROFF_TIME, args);
        }

        // 设置报警上限
        public void SetLimitUp(ushort value)
        {
            byte[] args = new byte[2];
            args[0] = (byte)(value >> 8);
            args[1] = (byte)(value & 0xFF);

            SendCommand((byte)CMD_TYPE.CMD_SET_LIMIT_UP, args);
        }

        // 设置报警下限
        public void SetLimitDown(ushort value)
        {
            byte[] args = new byte[2];
            args[0] = (byte)(value >> 8);
            args[1] = (byte)(value & 0xFF);

            SendCommand((byte)CMD_TYPE.CMD_SET_LIMIT_DOWN, args);
        }

        // 设置比较值
        public void SetLimitCompare(ushort value)
        {
            byte[] args = new byte[2];
            args[0] = (byte)(value >> 8);
            args[1] = (byte)(value & 0xFF);

            SendCommand((byte)CMD_TYPE.CMD_SET_LIMIT_CMP, args);
        }

        // 进入远程测量模式
        public void EnterRemoteMode()
        {
            SendCommand((byte)CMD_TYPE.CMD_ENTER_REMOTE);
        }

        // 退出远程测量模式
        public void ExitRemoteMode()
        {
            SendCommand((byte)CMD_TYPE.CMD_EXIT_REMOTE);
        }

        // 从远程测量数据中解析测量值
        public float ParseRemoteMeasurement(byte[] data, out bool direction)
        {
            // 数据格式: AA 06 0A 00 00 8B 00 64 9D EF
            // 06 - 数据长度
            // 0A - 命令
            // 00 - 方向 (01顺时针, 00逆时针)
            // 00 8B - 测量值
            // 00 64 - 放大倍数

            if (data.Length < 6)
            {
                throw new ArgumentException("无效的远程测量数据");
            }

            direction = data[2] == 0x01;

            ushort rawValue = (ushort)((data[3] << 8) | data[4]);
            ushort scale = (ushort)((data[5] << 8) | data[6]);

            if (scale == 0)
            {
                throw new InvalidOperationException("放大倍数不能为零");
            }

            return (float)rawValue / scale;
        }
    }
}
